﻿
#if UNITY_EDITOR

namespace Blaze.Security
{
    using System;
    using System.IO;
    using System.Text;
    using Ionic.Zip;

    /// <summary>
    /// 使用<see cref="BlowFish"/>加密后的<see cref="Stream"/>实现。
    /// </summary>
    public class EncryptionStream : MemoryStream
    {
        public static int CompressionLevel = 9;
        public static byte[] Key = Encoding.ASCII.GetBytes("Blaze");
        public static byte Offset = 21;
        public static byte[] PlainHeader = Encoding.ASCII.GetBytes("FWS");
        public static byte[] ZipHeader = Encoding.ASCII.GetBytes("CWS");
        public static int ZipMinSize = 2*1024; //小于2k不压缩

        public EncryptionStream(Stream textStream)
            : base(getCipherBytes(textStream))
        {
        }

        private static byte[] encrypt(byte[] textBytes)
        {
            for (var i = 0; i < textBytes.Length; i++)
            {
                textBytes[i] ^= Key[i%Key.Length];
                textBytes[i] = (byte) ((textBytes[i] + Offset)%256);
            }
            return textBytes;
        }

        private static byte[] getCipherBytes(Stream textStream)
        {
            var textBytes = new byte[textStream.Length];
            textStream.Read(textBytes, 0, textBytes.Length);

            byte[] header;
            if (textBytes.Length >= ZipMinSize)
            {
                header = ZipHeader;
                var memoryStream = new MemoryStream();
                using (var zipStream = new ZipOutputStream(memoryStream))
                {
                    zipStream.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
                    var entry = zipStream.PutNextEntry("_");
                    entry.SetEntryTimes(DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
                    entry.EmitTimesInUnixFormatWhenSaving = false;
                    entry.EmitTimesInWindowsFormatWhenSaving = false;
                    zipStream.Write(textBytes, 0, textBytes.Length);
                }
                textBytes = memoryStream.ToArray();
            }
            else
            {
                header = PlainHeader;
            }

            var cipherBytes = encrypt(textBytes);
            var retStream = new MemoryStream();
            //写入文件头
            retStream.Write(header, 0, header.Length);
            //写入原文件长度
            var lengthBytes = BitConverter.GetBytes((int) textStream.Length);
            retStream.Write(lengthBytes, 0, lengthBytes.Length);
            //写入加密后的内容
            retStream.Write(cipherBytes, 0, cipherBytes.Length);
            return retStream.ToArray();
        }
    }
}

#endif